require 'class/pack'
require 'class/card'
require 'module/colour'
require 'module/valour'

# begin
#   Author  : atila camurca
#   Date    : 16/03/2010
#   Version : 1.0
#     Description
#       a class with the initialization, rules and commands from uno.
#       classe contendo a inicialização, regras e comandos do uno.
# end

class AppEngine
  attr_accessor :pack, :players

  def initialize
    @pack = Pack.new
    @players = Array.new
  end

  # inicia o jogo.
  def init_game
    puts "Inicializando o Jogo . . ." # just for debug
    temp_pile = create_pile
    shuffle_pile(temp_pile)
    puts "Done." # just for debug
  end

  # begin
  #   Author  : atila camurca
  #   Date    : 14/03/2010
  #   Version : 1.0
  #   Return  : Array<Card>
  #     Description
  #       Create a deck with all the cards.
  #       Cria um baralho com todas as cartas.
  # end
  def create_pile
    # DONE criar um baralho inserindo na pilha.
    # TODO melhorar a performance do metodo.
    t_ini = Time.now
    temp_pile = Array.new
    for h in (Colour::Vermelho .. Colour::Amarelo)
      card = Card.new(h, 0)
      # @pack.pile.push(card)
      temp_pile.push(card)
    end
    # criar 4 cartas com valores variando de 0 a 9 e Valour::Jump a Valour::Inverse
    for i in (1 .. 12) # itera as 12 vezes
      for j in (Colour::Vermelho .. Colour::Amarelo) # cria 4 cartas com o valor de i
        card = Card.new(j, i)
        temp_pile.push(card, card)
        # @pack.pile.push(card, card)
      end
    end
    # criar 2 cartas com valores especiais
    for k in (Valour::Plus_4 .. Valour::Ch_colour)
      card = Card.new(Colour::Preto, k)
      temp_pile.push(card, card, card, card)
      # @pack.pile.push(card, card, card, card)
    end
    t_fim = Time.now
    tempo_execucao(t_ini, t_fim, "Criar Pilha.")
    return temp_pile
  end

  # begin
  #   Author  : atila camurca
  #   Date    : 14/03/2010
  #   Version : 1.0
  #     Description
  #       Shuffle the cards from the pile.
  #       Embaralha as cartas da pilha.
  # end
  def shuffle_pile pile
    # DONE embaralhar as cartas da pilha.
    # remover aleatoriamente uma carta da pilha e inserir randomicamente em @pack.pile
    t_ini = Time.now
    for index in (0 ... pile.size)
      # choice_1 = rand(index)
      choice = rand(pile.size)
      if index%2 == 0
        # se for par inseri a carta no fim do Array
        @pack.pile.push(pile.delete_at(choice))
      else
        # se for impar inseri a carta no inicio do Array
        @pack.pile.unshift(pile.delete_at(choice))
      end
    end
    t_fim = Time.now
    tempo_execucao(t_ini, t_fim, "Embaralhar Pilha.")
  end

  # begin
  #   Author  : atila camurca
  #   Date    : 17/03/2010
  #   Version : 1.0
  #   Return  : Um Array<Card> embaralhado.
  #     Description
  #       Shuffle the cards from a determined pile.
  #       Embaralha as cartas de uma determinada pilha.
  # end
  def shuffle_deck deck
    sh_deck = Array.new
    for index in (0 ... deck.size)
      choice = rand(deck.size)
      if index%2 == 0
        sh_deck.push(deck.delete_at(choice))
      else
        sh_deck.unshift(deck.delete_at(choice))
      end
    end
    return sh_deck
  end

  # begin
  #   Author  : atila camurca
  #   Date    : 16/03/2010
  #   Version : 1.0
  #   Param   : player Obj que representa o jogador.
  #     Description
  #       Create a player.
  #       Criar um jogador.
  # end
  def create_player(player)
    @players.push(player)
  end

  # begin
  #   Author  : atila camurca
  #   Date    : 17/03/2010
  #   Version : 1.0
  #   Param   : *players a single player or an -n- players you wish.
  #     Description
  #       Create dynamically players. You can pass the players as:
  #         create_players(player1, player2, player3)
  #       or
  #         create_players(player1)
  # end
  def create_players(*players)
    players.each { |player| @players.push(player) }
  end

  # begin
  #   Author    : atila camurca
  #   Date      : 16/03/2010
  #   Modified  : 17/03/2010
  #   Version   : 1.0
  #     Description
  #       Give the cards to all players.
  #       Distribui as cartas para todos os jogadores.
  # end
  def dealing_cards
    t_ini = Time.now
    for i in (0 ... @players.size)
      choice = rand(@players.size)
      7.times do
        @players[i].hand.push(@pack.pile.delete_at(choice))
      end
    end
    t_fim = Time.now
    tempo_execucao(t_ini, t_fim, "Dar as cartas aos jogadores.")
  end

  # begin
  #   Author  : atila camurca
  #   Date    : 16/03/2010
  #   Version : 1.0
  #   Param   : index O indice do jogador
  #     Description
  #       Give the player's hand by the index.
  #       Distribui a mão para o jogador a partir do indice.
  #     Deprecated use dealing_cards
  # end
  def dealing_cards_old(index)
    begin
      hand = Array.new
      if index%2 == 0
        for i in (0 ... 7)
          hand.push(@pack.pile.delete_at(i))
        end
      else
        for j in (0 ... 7)
          hand.unshift(@pack.pile.delete_at(j))
        end
      end
      @players[i].hand.concat(hand)
      return index
    rescue Exception => ex
      return nil
    end
  end

  # begin
  #   Author  : atila camurca
  #   Date    : 17/03/2010
  #   Version : 1.0
  #     Description
  #       Take off a card from the player's hand and throw in dead pile.
  #       Retira a carta da mão do jogador e joga no morto.
  # end
  def from_hand_to_dead(player_index, card_index)
    drop = @players[player_index].hand.delete_at(card_index)
    @pack.dead.push(drop)
  end

  # begin
  #   Author  : atila camurca
  #   Date    : 17/03/2010
  #   Version : 1.0
  #     Description
  #       When the main pile is out of cards, shuffle and remove the cards from dead pile and put into the main pile again.
  #       Quando a pilha principal está sem cartas, embaralha e remove todas as cartas do morto and devolve à pilha principal novamente.
  # end
  def from_dead_to_pile
    tmp_deck = shuffle_deck(@pack.dead)
    @pack.pile.concat(tmp_deck)
    @pack.dead.clear
  end

  # begin
  #   Author  : atila camurca
  #   Date    : 17/03/2010
  #   Version : 1.0
  #   Param   : player_index the index of the player
  #     Description
  #       If a player throw a wrong card in the pile, he's penalized, adding plus two cards in hand and passing your turn.
  #       Se o jogador jogar uma carta errada na pilha, ele é penalizado, adicionando mais duas cartas na mão e passando a vez.
  # end
  def noob_player(player_index)
    @players[player_index].concat(@pack.pile.slice!(0, 2))
  end

  # just for tests ...
  # só para testes ...
  def show_pack
    t_ini = Time.now
    for i in (0 ... @pack.pile.size)
      case @pack.pile[i].valour
      when 0 .. 9
        print @pack.pile[i].valour, " : "
      when 10
        print "jump : "
      when 11
        print "+ 2 : "
      when 12
        print "inverse : "
      when 13
        print "+ 4 : "
      when 14
        print "change colour : "
      end
      
      case @pack.pile[i].colour
      when Colour::Vermelho
        puts "vermelho"
      when Colour::Azul
        puts "azul"
      when Colour::Verde
        puts "verde"
      when Colour::Amarelo
        puts "amarelo"
      else
        puts "preto"
      end
    end
    puts @pack.pile.size, "\n"
    t_fim = Time.now
    tempo_execucao(t_ini, t_fim, "Mostrar Pilha.")
  end

  # just for tests ...
  # só para testes ...
  def show_players_hand
    t_ini = Time.now
    for i in (0 ... @players.size)
      puts "Jogador: #{@players[i].name}", "\n"
      for j in (0 ... @players[i].hand.size)
        case @players[i].hand[j].valour
        when 0 .. 9
          print @players[i].hand[j].valour, " : "
        when 10
          print "jump : "
        when 11
          print "+ 2 : "
        when 12
          print "inverse : "
        when 13
          print "+ 4 : "
        when 14
          print "change colour : "
        end

        case @players[i].hand[j].colour
        when Colour::Vermelho
          puts "vermelho"
        when Colour::Azul
          puts "azul"
        when Colour::Verde
          puts "verde"
        when Colour::Amarelo
          puts "amarelo"
        else
          puts "preto"
        end
      end
      puts "\n"
    end
    puts "Número de jogadores: #{@players.size}", "\n"
    t_fim = Time.now
    tempo_execucao(t_ini, t_fim, "Mostrar Pilha.")
  end

  def tempo_execucao(t_ini, t_fim, msg)
    puts "\t==> Tempo de Execução: #{t_fim - t_ini} - #{msg}", "\n"
  end

  private :create_pile, :shuffle_pile, :tempo_execucao
end
